You:
Fork the existing page "railroad baron!!".

Fetch its current HTML from https://tonkworks.com/page/railroad%20baron!!/raw/ and return an enhanced version as ONE complete, self-contained HTML document. Keep what already works.

What I want changed:
- make it so that trains stop running if track is removed/damaged; make it so you can buy differnt trains (with different speeds/power. Make it so you can connect special resources eg. (rubber farms to tire factories to make money). Make it so bridges cost more.

I built an enhanced Railroad Baron with track damage stopping trains, multiple purchasable train types with distinct stats, resource-chain gameplay (rubber→tires, iron→steel, oil→fuel), and expensive water bridges.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Railroad Baron!!</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
  :root { --gold:#d4a017; --panel:rgba(15,15,30,0.92); }
  * { box-sizing: border-box; }
  html,body { margin:0; padding:0; background:#0b0b18; color:#eee;
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif; overflow:hidden; height:100%; }
  #hud { position:fixed; top:0; left:0; right:0; padding:8px; background:var(--panel);
    z-index:10; display:flex; gap:8px; align-items:center; flex-wrap:wrap;
    border-bottom:2px solid #333; }
  #hud .stat { padding:6px 12px; background:#1e1e38; border-radius:4px; font-weight:600; }
  #hud .stat.goal { background:#3a2a0e; color:var(--gold); }
  #hud button { padding:6px 12px; background:#3a3a5a; color:white; border:none;
    border-radius:4px; cursor:pointer; font-size:14px; font-family:inherit; }
  #hud button:hover { background:#4a4a7a; }
  #hud button.active { background:var(--gold); color:#000; font-weight:700; }
  #wrap { position:relative; margin-top:56px; display:flex; justify-content:center; }
  #canvas { display:block; background:#223; image-rendering: pixelated; touch-action:none;
    box-shadow:0 6px 30px rgba(0,0,0,0.6); }
  #msg { position:fixed; bottom:8px; left:8px; right:8px; text-align:center; padding:8px;
    background:rgba(0,0,0,0.75); border-radius:6px; font-size:14px; pointer-events:none;
    border:1px solid #333; }
  #shop { position:fixed; top:60px; right:8px; background:var(--panel); padding:10px;
    border-radius:8px; display:none; width:260px; z-index:15; border:1px solid #444; }
  #shop.open { display:block; }
  #shop h3 { margin:0 0 8px 0; color:var(--gold); }
  .train-opt { padding:8px; margin:6px 0; background:#242444; border-radius:5px;
    cursor:pointer; border:2px solid transparent; transition:0.15s; }
  .train-opt:hover { background:#333366; }
  .train-opt.selected { border-color:var(--gold); background:#3a3a5a; }
  .train-opt.disabled { opacity:0.4; cursor:not-allowed; }
  .train-opt .name { font-weight:700; }
  .train-opt .stats { font-size:12px; color:#aaa; margin-top:2px; }
  #help { position:fixed; top:60px; left:8px; right:8px; bottom:40px; background:var(--panel);
    padding:20px; border-radius:8px; z-index:30; display:none; overflow:auto;
    border:1px solid #444; }
  #help.open { display:block; }
  #help h2 { color:var(--gold); margin-top:0; }
  #help h3 { color:#7bed9f; }
  #help .close { float:right; cursor:pointer; font-size:24px; color:#aaa; }
  #tooltip { position:fixed; background:rgba(0,0,0,0.92); color:#fff; padding:6px 10px;
    border-radius:5px; font-size:12px; pointer-events:none; display:none; z-index:20;
    border:1px solid var(--gold); white-space:nowrap; }
  .kb { background:#333; padding:1px 6px; border-radius:3px; font-family:monospace; border:1px solid #555; }
  #victory { position:fixed; inset:0; display:none; align-items:center; justify-content:center;
    background:rgba(0,0,0,0.85); z-index:40; flex-direction:column; }
  #victory.on { display:flex; }
  #victory h1 { color:var(--gold); font-size:64px; text-shadow:0 0 30px var(--gold); margin:0; }
  #victory p { font-size:20px; }
  #victory button { padding:10px 24px; font-size:16px; background:var(--gold); color:#000;
    border:none; border-radius:6px; cursor:pointer; font-weight:700; }
</style>
</head>
<body>
<div id="hud">
  <div class="stat">πŸ’° $<span id="money">500</span></div>
  <div class="stat">πŸš‚ <span id="trainCount">0</span></div>
  <div class="stat">πŸ“¦ <span id="delivered">0</span></div>
  <div class="stat goal">πŸ† $<span id="goal">5000</span></div>
  <button id="btnBuild" class="active">πŸ”¨ Build Track <span class="kb">1</span></button>
  <button id="btnBuy">πŸš‚ Buy Train <span class="kb">2</span></button>
  <button id="btnDemo">πŸ’₯ Demolish <span class="kb">3</span></button>
  <button id="btnHelp">❓ Help</button>
  <button id="btnNew">πŸ”„ New Map</button>
</div>
<div id="shop"></div>
<div id="wrap"><canvas id="canvas"></canvas></div>
<div id="msg">Click two cities or resource buildings to lay track between them.</div>
<div id="tooltip"></div>
<div id="help">
  <span class="close" id="helpClose">βœ•</span>
  <h2>πŸš‚ Railroad Baron!! β€” Guide</h2>
  <p><b>Goal:</b> Reach <b>$5,000</b> to become a Rail Baron.</p>
  <h3>πŸ”¨ Building Track</h3>
  <p>Click a city/building, then another. The cheapest route is auto-planned.
     Grass $10 Β· Forest $15 Β· Mountain $40 Β· <b>Water Bridge $150</b> (avoid unless necessary).</p>
  <h3>πŸš‚ Buying Trains</h3>
  <p>Pick a train from the shop, then click two <i>connected</i> nodes.
     Faster/higher-capacity trains earn more per trip but cost more upfront.</p>
  <ul>
    <li><b>Steam Engine</b> β€” cheap, slow, small</li>
    <li><b>Diesel</b> β€” balanced middle-tier</li>
    <li><b>Electric</b> β€” fast, premium</li>
    <li><b>Freight Hauler</b> β€” huge capacity, slow</li>
  </ul>
  <h3>🏭 Resource Chains (big bonus!)</h3>
  <p>Connect a producer to its matching consumer for major payouts:</p>
  <ul>
    <li>🌴 Rubber Farm β†’ βš™οΈ Tire Factory</li>
    <li>⛏️ Iron Mine β†’ 🏭 Steel Mill</li>
    <li>πŸ›’οΈ Oil Well β†’ βš—οΈ Refinery</li>
    <li>Factories/Refineries can then feed πŸ™οΈ Cities for more profit</li>
  </ul>
  <h3>πŸ’₯ Demolition &amp; Damage</h3>
  <p>Removing track refunds a fraction of its cost.
     <b>Any train whose route uses that tile will attempt to reroute; if none exists, it stops running until you rebuild!</b></p>
  <h3>Hotkeys</h3>
  <p><span class="kb">1</span> Build Β· <span class="kb">2</span> Buy Β· <span class="kb">3</span> Demolish Β· <span class="kb">Esc</span> Cancel selection</p>
</div>
<div id="victory">
  <h1>πŸ† RAIL BARON!</h1>
  <p>You built a $5,000 empire.</p>
  <button id="playOn">Keep Playing</button>
</div>

<script>
// ============ CONFIG ============
const GRID_W = 22, GRID_H = 14;
let TILE = 42;

const T = { GRASS:0, FOREST:1, MOUNTAIN:2, WATER:3 };
const TERRAIN_COST   = [10, 15, 40, 150];     // last is bridge cost (expensive!)
const TERRAIN_COLOR  = ['#3d6b3a', '#254a1f', '#7d6a4a', '#2a5a8a'];
const TERRAIN_NAME   = ['Grass','Forest','Mountain','Water (Bridge)'];

const NODES = {
  city:        { name:'City',         color:'#f0f0f0', text:'#222', icon:'πŸ™',  accepts:['*'] },
  rubberFarm:  { name:'Rubber Farm',  color:'#5a8a3a', text:'#fff', icon:'🌴', produces:'rubber' },
  tireFactory: { name:'Tire Factory', color:'#222',    text:'#fff', icon:'βš™',  accepts:['rubber'], produces:'tires' },
  ironMine:    { name:'Iron Mine',    color:'#8a6a4a', text:'#fff', icon:'⛏',  produces:'iron' },
  steelMill:   { name:'Steel Mill',   color:'#8f8f9a', text:'#000', icon:'🏭', accepts:['iron'], produces:'steel' },
  oilWell:     { name:'Oil Well',     color:'#1a1a1a', text:'#fff', icon:'πŸ›’',  produces:'oil' },
  refinery:    { name:'Refinery',     color:'#8a3a8a', text:'#fff', icon:'βš—',  accepts:['oil'], produces:'fuel' },
};

const TRAIN_TYPES = {
  steam:    { name:'Steam Engine',   cost:200,  speed:0.030, capacity:1, color:'#d4a017', icon:'πŸš‚' },
  diesel:   { name:'Diesel',         cost:500,  speed:0.055, capacity:2, color:'#4a90e2', icon:'πŸš„' },
  electric: { name:'Electric',       cost:1000, speed:0.085, capacity:3, color:'#7bed9f', icon:'πŸš…' },
  freight:  { name:'Freight Hauler', cost:800,  speed:0.035, capacity:5, color:'#e67e22', icon:'🚞' },
};

const GOAL = 5000;

// ============ STATE ============
let money, delivered, tickCount, mode, selectedNodeA, selectedTrainType,
    grid, nodes, tracks, trains, floaters, mouseTile, mouseXY, baronReached;

function K(x,y){ return x+','+y; }

function newMap(){
  money = 500;
  delivered = 0;
  tickCount = 0;
  mode = 'build';
  selectedNodeA = null;
  selectedTrainType = 'steam';
  grid = [];
  nodes = [];
  tracks = new Map();
  trains = [];
  floaters = [];
  mouseTile = null;
  baronReached = false;
  document.getElementById('victory').classList.remove('on');

  // Base terrain
  for (let y=0; y<GRID_H; y++){
    grid[y] = [];
    for (let x=0; x<GRID_W; x++){
      const r = Math.random();
      let t;
      if (r < 0.62) t = T.GRASS;
      else if (r < 0.82) t = T.FOREST;
      else if (r < 0.94) t = T.MOUNTAIN;
      else t = T.WATER;
      grid[y][x] = t;
    }
  }
  // Add a winding river
  let rx = 3 + Math.floor(Math.random()*(GRID_W-6));
  for (let y=0; y<GRID_H; y++){
    grid[y][rx] = T.WATER;
    if (Math.random() < 0.35 && rx+1 < GRID_W) grid[y][rx+1] = T.WATER;
    if (Math.random() < 0.5) rx += Math.random()<0.5?-1:1;
    rx = Math.max(2, Math.min(GRID_W-3, rx));
  }

  // Place nodes with spacing
  const nodePlan = [
    'city','city','city','city','city',
    'rubberFarm','tireFactory',
    'ironMine','steelMill',
    'oilWell','refinery'
  ];
  for (const type of nodePlan){
    for (let attempt=0; attempt<200; attempt++){
      const nx = 1 + Math.floor(Math.random()*(GRID_W-2));
      const ny = 1 + Math.floor(Math.random()*(GRID_H-2));
      if (grid[ny][nx] === T.WATER) continue;
      if (grid[ny][nx] === T.MOUNTAIN) continue;
      const tooClose = nodes.some(n => Math.abs(n.x-nx)<=2 && Math.abs(n.y-ny)<=2);
      if (tooClose) continue;
      // Clear the immediate tile to grass for placement
      grid[ny][nx] = T.GRASS;
      nodes.push({ x:nx, y:ny, type, id:nodes.length });
      break;
    }
  }
  updateHUD();
  fitCanvas();
  showMessage('New map! Build track between nodes to move goods.');
}

// ============ PATHFINDING ============
function nodeAt(x,y){ return nodes.find(n => n.x===x && n.y===y) || null; }

function findTrackPath(start, end){
  // A* honoring terrain cost; existing tracks are cheap; endpoint nodes allowed
  const sKey = K(start.x,start.y), eKey = K(end.x,end.y);
  const gScore = new Map(); gScore.set(sKey, 0);
  const cameFrom = new Map();
  const open = [[0, sKey]];
  while (open.length){
    open.sort((a,b)=>a[0]-b[0]);
    const [_, cur] = open.shift();
    if (cur === eKey){
      const path = []; let c = cur;
      while (c){ const [x,y]=c.split(',').map(Number); path.unshift({x,y}); c = cameFrom.get(c); }
      return path;
    }
    const [cx,cy] = cur.split(',').map(Number);
    const g = gScore.get(cur);
    for (const [dx,dy] of [[1,0],[-1,0],[0,1],[0,-1]]){
      const nx=cx+dx, ny=cy+dy;
      if (nx<0||nx>=GRID_W||ny<0||ny>=GRID_H) continue;
      const nkey = K(nx,ny);
      const nAt = nodeAt(nx,ny);
      // Can't pass through other nodes except the endpoint
      if (nAt && nkey !== eKey) continue;
      let cost;
      if (nAt) cost = 1;
      else if (tracks.has(nkey)) cost = 1;
      else cost = TERRAIN_COST[grid[ny][nx]];
      const ng = g + cost;
      if (ng < (gScore.get(nkey) ?? Infinity)){
        gScore.set(nkey, ng);
        cameFrom.set(nkey, cur);
        const h = Math.abs(nx-end.x)+Math.abs(ny-end.y);
        open.push([ng+h, nkey]);
      }
    }
  }
  return null;
}

function findTrainPath(start, end){
  // BFS across track-tiles + node tiles
  const sKey = K(start.x,start.y), eKey = K(end.x,end.y);
  const cameFrom = new Map(); cameFrom.set(sKey, null);
  const q = [sKey];
  while (q.length){
    const cur = q.shift();
    if (cur === eKey){
      const path = []; let c = cur;
      while (c !== null){
        const [x,y] = c.split(',').map(Number); path.unshift({x,y});
        c = cameFrom.get(c);
      }
      return path;
    }
    const [cx,cy] = cur.split(',').map(Number);
    for (const [dx,dy] of [[1,0],[-1,0],[0,1],[0,-1]]){
      const nx=cx+dx, ny=cy+dy;
      if (nx<0||nx>=GRID_W||ny<0||ny>=GRID_H) continue;
      const nkey = K(nx,ny);
      if (cameFrom.has(nkey)) continue;
      const nAt = nodeAt(nx,ny);
      const passable = tracks.has(nkey) || (nAt && (nkey===eKey || nkey===sKey));
      if (!passable) continue;
      cameFrom.set(nkey, cur);
      q.push(nkey);
    }
  }
  return null;
}

// ============ ACTIONS ============
function pathCost(path){
  let c = 0;
  for (const p of path){
    const k = K(p.x,p.y);
    if (nodeAt(p.x,p.y)) continue;
    if (tracks.has(k)) continue;
    c += TERRAIN_COST[grid[p.y][p.x]];
  }
  return c;
}

function buildTrackBetween(a, b){
  if (a.id === b.id){ showMessage('Choose a different endpoint.'); return; }
  const path = findTrackPath(a,b);
  if (!path){ showMessage('No possible route.'); return; }
  const cost = pathCost(path);
  if (cost > money){ showMessage(`Can't afford β€” need $${cost}, have $${money}.`); return; }
  for (const p of path){
    const k = K(p.x,p.y);
    if (nodeAt(p.x,p.y)) continue;
    if (!tracks.has(k)){
      tracks.set(k, { isBridge: grid[p.y][p.x] === T.WATER });
    }
  }
  money -= cost;
  showMessage(`Built ${path.length-2} tiles for $${cost}. ${cost>=150?'⚠ Bridges are expensive!':''}`);
  updateHUD();
}

function computeTripPay(train){
  const a = nodes[train.endpointA], b = nodes[train.endpointB];
  const dist = train.route.length;
  const cap = TRAIN_TYPES[train.type].capacity;
  let pay = 6 + dist * 1.6;
  const aI = NODES[a.type], bI = NODES[b.type];
  const matchAB = aI.produces && bI.accepts && (bI.accepts.includes('*')||bI.accepts.includes(aI.produces));
  const matchBA = bI.produces && aI.accepts && (aI.accepts.includes('*')||aI.accepts.includes(bI.produces));
  if (matchAB) pay += 12 + dist * 2.5;
  if (matchBA) pay += 12 + dist * 2.5;
  // Extra bonus for perfect resource chain
  if (matchAB && matchBA) pay *= 1.3;
  return Math.max(4, Math.floor(pay * (0.6 + 0.4*cap)));
}

function buyTrainBetween(a, b){
  if (a.id === b.id){ showMessage('Choose two different nodes.'); return; }
  const spec = TRAIN_TYPES[selectedTrainType];
  if (money < spec.cost){ showMessage(`Need $${spec.cost} for ${spec.name}.`); return; }
  const path = findTrainPath(a,b);
  if (!path){ showMessage('Those nodes are not connected by track.'); return; }
  money -= spec.cost;
  const tr = {
    type: selectedTrainType,
    endpointA: a.id,
    endpointB: b.id,
    route: path,
    seg: 0,       // index into route (from - the segment starts here)
    prog: 0,      // 0..1 progress toward route[seg+1]
    dir: 1,
    stopped: false,
    id: trains.length,
  };
  tr.pay = computeTripPay(tr);
  trains.push(tr);
  showMessage(`Bought ${spec.name}! Payout $${tr.pay}/trip.`);
  updateHUD();
}

function demolishAt(x,y){
  const k = K(x,y);
  if (!tracks.has(k)){ showMessage('No track there.'); return; }
  const tile = tracks.get(k);
  const original = tile.isBridge ? 150 : TERRAIN_COST[grid[y][x]];
  const refund = Math.max(1, Math.floor(original * 0.25));
  tracks.delete(k);
  money += refund;

  // Break/reroute any train that uses this tile
  let broken = 0, rerouted = 0;
  for (const tr of trains){
    if (tr.route.some(p => p.x===x && p.y===y)){
      const a = nodes[tr.endpointA], b = nodes[tr.endpointB];
      const np = findTrainPath(a,b);
      if (np){
        tr.route = np;
        tr.seg = Math.min(tr.seg, np.length-2);
        tr.prog = 0;
        tr.stopped = false;
        tr.pay = computeTripPay(tr);
        rerouted++;
      } else {
        tr.stopped = true;
        broken++;
      }
    }
  }
  let msg = `Removed track, refund $${refund}.`;
  if (broken) msg += ` 🚨 ${broken} train${broken>1?'s':''} stranded!`;
  if (rerouted) msg += ` ${rerouted} rerouted.`;
  showMessage(msg);
  updateHUD();
}

// ============ TRAIN UPDATE ============
function tickTrains(){
  for (const tr of trains){
    if (tr.stopped) continue;
    // Safety: ensure current segment still valid (both endpoints have track/node)
    if (tr.seg < 0 || tr.seg >= tr.route.length-1){
      tr.seg = Math.max(0, Math.min(tr.route.length-2, tr.seg));
    }
    const from = tr.route[tr.seg];
    const to = tr.route[tr.seg + 1];
    // Validate both tiles exist as track or node β€” else stop
    const fromOK = !!(nodeAt(from.x,from.y) || tracks.has(K(from.x,from.y)));
    const toOK = !!(nodeAt(to.x,to.y) || tracks.has(K(to.x,to.y)));
    if (!fromOK || !toOK){
      // Attempt reroute
      const a = nodes[tr.endpointA], b = nodes[tr.endpointB];
      const np = findTrainPath(a,b);
      if (np){ tr.route = np; tr.seg = 0; tr.prog = 0; tr.pay = computeTripPay(tr); }
      else { tr.stopped = true; }
      continue;
    }
    tr.prog += TRAIN_TYPES[tr.type].speed;
    while (tr.prog >= 1){
      tr.prog -= 1;
      tr.seg += tr.dir;
      if (tr.seg >= tr.route.length - 1){
        tr.seg = tr.route.length - 1;
        tr.dir = -1;
        arriveAt(tr, tr.endpointB);
      } else if (tr.seg <= 0){
        tr.seg = 0;
        tr.dir = 1;
        arriveAt(tr, tr.endpointA);
      }
    }
  }
  // Update floaters
  for (let i = floaters.length-1; i>=0; i--){
    floaters[i].t += 1;
    floaters[i].y -= 0.6;
    if (floaters[i].t > 60) floaters.splice(i,1);
  }
}

function arriveAt(tr, endpointId){
  money += tr.pay;
  delivered++;
  const n = nodes[endpointId];
  floaters.push({ x: n.x*TILE + TILE/2, y: n.y*TILE, txt:`+$${tr.pay}`, t:0, color:TRAIN_TYPES[tr.type].color });
  if (money >= GOAL && !baronReached){
    baronReached = true;
    document.getElementById('victory').classList.add('on');
  }
  updateHUD();
}

// ============ RENDERING ============
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

function fitCanvas(){
  const maxW = window.innerWidth - 20;
  const maxH = window.innerHeight - 120;
  TILE = Math.floor(Math.min(maxW/GRID_W, maxH/GRID_H));
  TILE = Math.max(24, Math.min(48, TILE));
  canvas.width = GRID_W * TILE;
  canvas.height = GRID_H * TILE;
}

function drawTerrain(){
  for (let y=0; y<GRID_H; y++){
    for (let x=0; x<GRID_W; x++){
      const t = grid[y][x];
      ctx.fillStyle = TERRAIN_COLOR[t];
      ctx.fillRect(x*TILE, y*TILE, TILE, TILE);
      if (t === T.FOREST){
        ctx.fillStyle = '#1a3a15';
        for (let i=0;i<3;i++){
          const dx = ((x*13+i*7)%TILE);
          const dy = ((y*11+i*5)%TILE);
          ctx.beginPath();
          ctx.arc(x*TILE+dx, y*TILE+dy, TILE*0.12, 0, Math.PI*2);
          ctx.fill();
        }
      } else if (t === T.MOUNTAIN){
        ctx.fillStyle = '#544230';
        ctx.beginPath();
        ctx.moveTo(x*TILE+TILE*0.15, y*TILE+TILE*0.85);
        ctx.lineTo(x*TILE+TILE*0.5,  y*TILE+TILE*0.20);
        ctx.lineTo(x*TILE+TILE*0.85, y*TILE+TILE*0.85);
        ctx.closePath(); ctx.fill();
        ctx.fillStyle = '#f0f0f0';
        ctx.beginPath();
        ctx.moveTo(x*TILE+TILE*0.42, y*TILE+TILE*0.34);
        ctx.lineTo(x*TILE+TILE*0.5,  y*TILE+TILE*0.20);
        ctx.lineTo(x*TILE+TILE*0.58, y*TILE+TILE*0.34);
        ctx.closePath(); ctx.fill();
      } else if (t === T.WATER){
        // subtle waves
        ctx.strokeStyle = 'rgba(255,255,255,0.15)';
        ctx.lineWidth = 1;
        const off = (tickCount*0.5 + (x+y)*10) % TILE;
        ctx.beginPath();
        ctx.moveTo(x*TILE, y*TILE+TILE*0.4+Math.sin(off*0.2)*2);
        ctx.lineTo(x*TILE+TILE, y*TILE+TILE*0.4+Math.cos(off*0.2)*2);
        ctx.stroke();
      }
    }
  }
  // grid overlay
  ctx.strokeStyle = 'rgba(0,0,0,0.18)';
  ctx.lineWidth = 1;
  for (let x=0;x<=GRID_W;x++){
    ctx.beginPath(); ctx.moveTo(x*TILE,0); ctx.lineTo(x*TILE,GRID_H*TILE); ctx.stroke();
  }
  for (let y=0;y<=GRID_H;y++){
    ctx.beginPath(); ctx.moveTo(0,y*TILE); ctx.lineTo(GRID_W*TILE,y*TILE); ctx.stroke();
  }
}

function hasTrackAt(x,y){
  return tracks.has(K(x,y)) || !!nodeAt(x,y);
}

function drawTracks(){
  // For each track tile, draw connections to neighbors
  for (const [key, tile] of tracks){
    const [x,y] = key.split(',').map(Number);
    const cx = x*TILE + TILE/2, cy = y*TILE + TILE/2;
    // Bridge base
    if (tile.isBridge){
      ctx.fillStyle = '#6a4a2a';
      ctx.fillRect(x*TILE+2, y*TILE+TILE*0.3, TILE-4, TILE*0.4);
      ctx.strokeStyle = '#3a2a1a';
      ctx.lineWidth = 1;
      ctx.strokeRect(x*TILE+2, y*TILE+TILE*0.3, TILE-4, TILE*0.4);
    }
    drawTrackTile(x,y,cx,cy);
  }
  // Also draw rails going into nodes (short stubs)
  for (const n of nodes){
    const cx = n.x*TILE+TILE/2, cy = n.y*TILE+TILE/2;
    drawTrackTile(n.x, n.y, cx, cy, true);
  }
}

function drawTrackTile(x,y,cx,cy,isNode=false){
  const neighbors = [
    {dx:1,dy:0}, {dx:-1,dy:0}, {dx:0,dy:1}, {dx:0,dy:-1}
  ];
  ctx.strokeStyle = '#2a2a2a';
  ctx.lineWidth = Math.max(3, TILE*0.12);
  for (const {dx,dy} of neighbors){
    const nx=x+dx, ny=y+dy;
    if (!hasTrackAt(nx,ny)) continue;
    if (isNode && !tracks.has(K(nx,ny))) continue; // node stubs only toward real tracks
    ctx.beginPath();
    ctx.moveTo(cx, cy);
    ctx.lineTo(cx + dx*TILE*0.5, cy + dy*TILE*0.5);
    ctx.stroke();
  }
  // Rail ties on top
  if (!isNode){
    ctx.strokeStyle = '#c0c0c0';
    ctx.lineWidth = Math.max(1, TILE*0.04);
    for (const {dx,dy} of neighbors){
      const nx=x+dx, ny=y+dy;
      if (!hasTrackAt(nx,ny)) continue;
      // parallel rail lines
      for (const off of [-1,1]){
        ctx.beginPath();
        const perp = { x:-dy*off*TILE*0.12, y:dx*off*TILE*0.12 };
        ctx.moveTo(cx+perp.x, cy+perp.y);
        ctx.lineTo(cx+dx*TILE*0.5+perp.x, cy+dy*TILE*0.5+perp.y);
        ctx.stroke();
      }
    }
  }
}

function drawNodes(){
  for (const n of nodes){
    const info = NODES[n.type];
    const x = n.x*TILE, y = n.y*TILE;
    ctx.fillStyle = info.color;
    ctx.strokeStyle = '#000';
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.roundRect(x+3, y+3, TILE-6, TILE-6, 4);
    ctx.fill(); ctx.stroke();
    ctx.fillStyle = info.text;
    ctx.font = `${Math.floor(TILE*0.55)}px sans-serif`;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(info.icon, x+TILE/2, y+TILE/2+1);
  }
}

function drawTrains(){
  for (const tr of trains){
    const spec = TRAIN_TYPES[tr.type];
    const from = tr.route[tr.seg];
    const to   = tr.route[tr.seg + (tr.dir>0?1:1)] || tr.route[tr.seg];
    // interpolate
    const nextIdx = Math.min(tr.route.length-1, tr.seg + 1);
    const a = tr.route[tr.seg];
    const b = tr.route[nextIdx];
    let fx = a.x + (b.x-a.x)*tr.prog;
    let fy = a.y + (b.y-a.y)*tr.prog;
    const px = fx*TILE + TILE/2;
    const py = fy*TILE + TILE/2;
    const size = TILE*0.55;
    // shadow
    ctx.fillStyle = 'rgba(0,0,0,0.4)';
    ctx.beginPath();
    ctx.ellipse(px, py+size*0.4, size*0.5, size*0.15, 0, 0, Math.PI*2);
    ctx.fill();
    // body
    ctx.fillStyle = tr.stopped ? '#666' : spec.color;
    ctx.strokeStyle = '#000';
    ctx.lineWidth = 2;
    const angle = Math.atan2(b.y-a.y, b.x-a.x);
    ctx.save();
    ctx.translate(px,py);
    ctx.rotate(angle);
    ctx.beginPath();
    ctx.roundRect(-size*0.5, -size*0.3, size, size*0.6, 4);
    ctx.fill(); ctx.stroke();
    // cab window
    ctx.fillStyle = 'rgba(0,0,0,0.5)';
    ctx.fillRect(size*0.1, -size*0.2, size*0.25, size*0.4);
    // smoke stack for steam / freight
    if (tr.type === 'steam' || tr.type==='freight'){
      ctx.fillStyle = '#000';
      ctx.fillRect(-size*0.35, -size*0.45, size*0.15, size*0.2);
    }
    ctx.restore();
    // stopped indicator
    if (tr.stopped){
      ctx.fillStyle = '#ff4444';
      ctx.font = `${Math.floor(TILE*0.3)}px sans-serif`;
      ctx.textAlign = 'center';
      ctx.fillText('⚠', px, py - size*0.6);
    }
  }
}

function drawPreview(){
  if (!mouseTile) return;
  if (mode==='build' || mode==='buy'){
    if (!selectedNodeA) return;
    const target = nodeAt(mouseTile.x, mouseTile.y);
    if (!target || target.id===selectedNodeA.id) return;
    const path = mode==='build' ? findTrackPath(selectedNodeA, target) : findTrainPath(selectedNodeA, target);
    if (!path){
      ctx.fillStyle = 'rgba(255,80,80,0.3)';
      ctx.fillRect(target.x*TILE, target.y*TILE, TILE, TILE);
      return;
    }
    let cost = 0;
    if (mode==='build') cost = pathCost(path);
    ctx.strokeStyle = mode==='build' ? 'rgba(255,215,0,0.85)' : 'rgba(120,255,180,0.85)';
    ctx.lineWidth = Math.max(3, TILE*0.15);
    ctx.beginPath();
    for (let i=0;i<path.length;i++){
      const p = path[i];
      if (i===0) ctx.moveTo(p.x*TILE+TILE/2, p.y*TILE+TILE/2);
      else ctx.lineTo(p.x*TILE+TILE/2, p.y*TILE+TILE/2);
    }
    ctx.stroke();
    if (mode==='build'){
      ctx.fillStyle = '#000';
      ctx.fillRect(mouseTile.x*TILE, mouseTile.y*TILE-18, 70, 16);
      ctx.fillStyle = cost>money ? '#ff5555' : '#ffd700';
      ctx.font = '12px sans-serif';
      ctx.textAlign='left';
      ctx.fillText(`$${cost}`, mouseTile.x*TILE+4, mouseTile.y*TILE-5);
    }
  } else if (mode==='demolish'){
    const k = K(mouseTile.x, mouseTile.y);
    if (tracks.has(k)){
      ctx.fillStyle = 'rgba(255,80,80,0.4)';
      ctx.fillRect(mouseTile.x*TILE, mouseTile.y*TILE, TILE, TILE);
    }
  }
  // Hover highlight over source node
  if (selectedNodeA){
    ctx.strokeStyle = '#ffd700';
    ctx.lineWidth = 3;
    ctx.strokeRect(selectedNodeA.x*TILE+2, selectedNodeA.y*TILE+2, TILE-4, TILE-4);
  }
}

function drawFloaters(){
  ctx.textAlign = 'center';
  ctx.font = `bold ${Math.floor(TILE*0.35)}px sans-serif`;
  for (const f of floaters){
    ctx.fillStyle = f.color;
    ctx.globalAlpha = 1 - f.t/60;
    ctx.fillText(f.txt, f.x, f.y);
    ctx.globalAlpha = 1;
  }
}

function draw(){
  tickCount++;
  ctx.fillStyle = '#000';
  ctx.fillRect(0,0,canvas.width,canvas.height);
  drawTerrain();
  drawTracks();
  drawNodes();
  drawTrains();
  drawPreview();
  drawFloaters();
}

// ============ INPUT ============
function getTileFromEvent(e){
  const rect = canvas.getBoundingClientRect();
  const cx = (e.touches?e.touches[0].clientX:e.clientX) - rect.left;
  const cy = (e.touches?e.touches[0].clientY:e.clientY) - rect.top;
  const x = Math.floor(cx / TILE);
  const y = Math.floor(cy / TILE);
  if (x<0||x>=GRID_W||y<0||y>=GRID_H) return null;
  return {x,y};
}

canvas.addEventListener('mousemove', (e)=>{
  mouseTile = getTileFromEvent(e);
  mouseXY = { x: e.clientX, y: e.clientY };
  updateTooltip();
});
canvas.addEventListener('mouseleave', ()=>{
  mouseTile = null;
  hideTooltip();
});

function updateTooltip(){
  if (!mouseTile){ hideTooltip(); return; }
  const n = nodeAt(mouseTile.x, mouseTile.y);
  const tt = document.getElementById('tooltip');
  if (n){
    const info = NODES[n.type];
    let txt = `<b>${info.name}</b>`;
    if (info.produces) txt += `<br>Produces: ${info.produces}`;
    if (info.accepts) txt += `<br>Accepts: ${info.accepts.join(', ')}`;
    tt.innerHTML = txt;
    tt.style.display = 'block';
    tt.style.left = (mouseXY.x + 12) + 'px';
    tt.style.top  = (mouseXY.y + 12) + 'px';
  } else if (tracks.has(K(mouseTile.x,mouseTile.y))){
    const isB = tracks.get(K(mouseTile.x,mouseTile.y)).isBridge;
    tt.innerHTML = isB ? '<b>Bridge</b><br>Fragile β€” costly to lose!' : '<b>Track</b>';
    tt.style.display = 'block';
    tt.style.left = (mouseXY.x + 12) + 'px';
    tt.style.top  = (mouseXY.y + 12) + 'px';
  } else {
    const t = grid[mouseTile.y][mouseTile.x];
    tt.innerHTML = `<b>${TERRAIN_NAME[t]}</b><br>Track cost: $${TERRAIN_COST[t]}`;
    tt.style.display = 'block';
    tt.style.left = (mouseXY.x + 12) + 'px';
    tt.style.top  = (mouseXY.y + 12) + 'px';
  }
}
function hideTooltip(){ document.getElementById('tooltip').style.display='none'; }

canvas.addEventListener('click', (e)=>{
  const tile = getTileFromEvent(e);
  if (!tile) return;
  if (mode === 'demolish'){
    demolishAt(tile.x, tile.y);
    return;
  }
  const n = nodeAt(tile.x, tile.y);
  if (!n){
    if (mode==='build' || mode==='buy'){
      showMessage('Click on a city or building.');
    }
    return;
  }
  if (!selectedNodeA){
    selectedNodeA = n;
    showMessage(`Selected ${NODES[n.type].name}. Now click destination.`);
  } else {
    if (mode==='build') buildTrackBetween(selectedNodeA, n);
    else if (mode==='buy') buyTrainBetween(selectedNodeA, n);
    selectedNodeA = null;
  }
});

// ============ UI ============
function setMode(m){
  mode = m;
  selectedNodeA = null;
  document.getElementById('btnBuild').classList.toggle('active', m==='build');
  document.getElementById('btnBuy').classList.toggle('active', m==='buy');
  document.getElementById('btnDemo').classList.toggle('active', m==='demolish');
  document.getElementById('shop').classList.toggle('open', m==='buy');
  if (m==='build') showMessage('Build: click two nodes. Auto-plans cheapest route.');
  if (m==='buy')   showMessage('Buy: pick a train type, then click two connected nodes.');
  if (m==='demolish') showMessage('Demolish: click any track tile. ⚠ Trains using it may stop!');
}

function renderShop(){
  const shop = document.getElementById('shop');
  let html = '<h3>πŸš‚ Train Shop</h3>';
  for (const key of Object.keys(TRAIN_TYPES)){
    const t = TRAIN_TYPES[key];
    const canBuy = money >= t.cost;
    const sel = key === selectedTrainType ? 'selected' : '';
    const dis = canBuy ? '' : 'disabled';
    html += `<div class="train-opt ${sel} ${dis}" data-key="${key}">
      <div class="name">${t.icon} ${t.name} β€” $${t.cost}</div>
      <div class="stats">Speed ${(t.speed*100).toFixed(1)} Β· Cap ${t.capacity}</div>
    </div>`;
  }
  shop.innerHTML = html;
  shop.querySelectorAll('.train-opt').forEach(el=>{
    el.addEventListener('click', ()=>{
      if (el.classList.contains('disabled')) return;
      selectedTrainType = el.dataset.key;
      renderShop();
    });
  });
}

function updateHUD(){
  document.getElementById('money').textContent = money;
  document.getElementById('trainCount').textContent = trains.length;
  document.getElementById('delivered').textContent = delivered;
  renderShop();
}

function showMessage(t){
  document.getElementById('msg').textContent = t;
}

document.getElementById('btnBuild').addEventListener('click', ()=>setMode('build'));
document.getElementById('btnBuy').addEventListener('click', ()=>setMode('buy'));
document.getElementById('btnDemo').addEventListener('click', ()=>setMode('demolish'));
document.getElementById('btnNew').addEventListener('click', newMap);
document.getElementById('btnHelp').addEventListener('click', ()=>{
  document.getElementById('help').classList.add('open');
});
document.getElementById('helpClose').addEventListener('click', ()=>{
  document.getElementById('help').classList.remove('open');
});
document.getElementById('playOn').addEventListener('click', ()=>{
  document.getElementById('victory').classList.remove('on');
});

document.addEventListener('keydown', (e)=>{
  if (e.key==='1') setMode('build');
  else if (e.key==='2') setMode('buy');
  else if (e.key==='3') setMode('demolish');
  else if (e.key==='Escape') { selectedNodeA = null; showMessage('Cancelled.'); }
});

window.addEventListener('resize', ()=>{
  fitCanvas();
});

// ============ GAME LOOP ============
function loop(){
  tickTrains();
  draw();
  requestAnimationFrame(loop);
}

// Polyfill roundRect on old canvases
if (!CanvasRenderingContext2D.prototype.roundRect){
  CanvasRenderingContext2D.prototype.roundRect = function(x,y,w,h,r){
    if (typeof r==='number') r = {tl:r,tr:r,br:r,bl:r};
    else if (typeof r==='object' && r.length){ r = {tl:r[0],tr:r[1],br:r[2],bl:r[3]}; }
    this.moveTo(x+r.tl, y);
    this.lineTo(x+w-r.tr, y);
    this.quadraticCurveTo(x+w,y,x+w,y+r.tr);
    this.lineTo(x+w, y+h-r.br);
    this.quadraticCurveTo(x+w,y+h,x+w-r.br,y+h);
    this.lineTo(x+r.bl, y+h);
    this.quadraticCurveTo(x,y+h,x,y+h-r.bl);
    this.lineTo(x, y+r.tl);
    this.quadraticCurveTo(x,y,x+r.tl,y);
    return this;
  };
}

newMap();
loop();
</script>
</body>
</html>